# -*- coding:utf-8 -*-
from typing import Callable, Any
import jsonpath
import allure
from unit_tools.exception_utils.exceptions import AssertTypeError
from unit_tools.db_connector.connectMysql import ConnectMysql
import operator
from unit_tools.log_util.recordlog import logs


class Assertions:

    @classmethod
    def status_code_assert(cls, expected_result, status_code):
        failure_count = 0
        if not isinstance(expected_result, int):
            expected_result = int(expected_result)
        if expected_result == status_code:
            logs.info(f'状态码断言成功：接口实际返回状态码 {status_code} == {expected_result}')
            allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(status_code)}', '状态码断言结果：成功！',
                          attachment_type=allure.attachment_type.TEXT)
        else:
            logs.error(f'状态码断言失败：接口实际返回状态码 {status_code} == {expected_result}')
            failure_count += 1
            allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(status_code)}', '状态码断言结果：失败！',
                          attachment_type=allure.attachment_type.TEXT)
        return failure_count

    @classmethod
    def contain_assert(cls, expected_result, response):
        failure_count = 0
        for assert_key, assert_value in expected_result.items():
            response_list = jsonpath.jsonpath(response, f'$..{assert_key}')
            if response_list and isinstance(response_list[0], str):
                response_str = ''.join(response_list)
                success_message = f'包含模式断言成功：预期结果【{assert_value}】存在实际结果【{response_str}】中'
                failure_message = f'包含模式断言失败：预期结果【{assert_value}】未存在，没有在实际结果【{response_str}】中找到'
                if assert_value in response_str:
                    logs.info(success_message)
                    allure.attach(f'预期结果：{str(assert_value)}\n实际结果：{str(response_str)}',
                                  '包含断言结果：成功！', attachment_type=allure.attachment_type.JSON)
                else:
                    failure_count += 1
                    logs.error(failure_message)
                    allure.attach(f'预期结果：{str(assert_value)}\n实际结果：{str(response_str)}',
                                  '包含断言结果：失败！', attachment_type=allure.attachment_type.JSON)
        return failure_count

    @classmethod
    def equal_assert(cls, expected_result, response):
        failure_count = 0
        if isinstance(response, dict) and isinstance(expected_result, dict):
            common_key = list(expected_result.keys() & response.keys())
            if common_key:
                common_key = common_key[0]
                new_actual_result = {common_key: response[common_key]}
                eq_assert = operator.eq(new_actual_result, expected_result)
                if eq_assert:
                    logs.info(f'相等断言成功：接口实际结果 {new_actual_result} == 预期结果：{expected_result}')
                    allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(new_actual_result)}',
                                  '相等断言结果：成功！', attachment_type=allure.attachment_type.JSON)
                else:
                    failure_count += 1
                    logs.error(f'相等断言失败：接口实际结果 {new_actual_result} != 预期结果：{expected_result}')
                    allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(new_actual_result)}',
                                  '相等断言结果：失败！', attachment_type=allure.attachment_type.JSON)
            else:
                failure_count += 1
                logs.error('相等断言失败：请检查yaml文件eq模式的预期结果或接口返回值是否正确！')

        return failure_count

    @classmethod
    def not_equal_assert(cls, expected_result, response):
        failure_count = 0
        if isinstance(response, dict) and isinstance(expected_result, dict):
            common_key = list(expected_result.keys() & response.keys())
            if common_key:
                common_key = common_key[0]
                new_actual_result = {common_key: response[common_key]}
                eq_assert = operator.ne(new_actual_result, expected_result)
                if eq_assert:
                    logs.info(f'不相等断言成功：接口实际结果 {new_actual_result} != 预期结果：{expected_result}')
                    allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(new_actual_result)}',
                                  '不相等断言结果：成功！', attachment_type=allure.attachment_type.JSON)
                else:
                    failure_count += 1
                    logs.error(f'不相等断言失败：接口实际结果 {new_actual_result} == 预期结果：{expected_result}')
                    allure.attach(f'预期结果：{str(expected_result)}\n实际结果：{str(new_actual_result)}',
                                  '不相等断言结果：失败！', attachment_type=allure.attachment_type.JSON)
            else:
                failure_count += 1
                logs.error('不相等断言失败：请检查yaml文件ne模式的预期结果或接口返回值是否正确！')

        return failure_count

    @classmethod
    def database_assert(cls, expected_result, status_code=None):
        failure_count = 0
        conn = ConnectMysql()
        db_value = conn.query(expected_result)
        if db_value is not None:
            logs.info('数据库断言成功！')
        else:
            failure_count += 1
            logs.error('数据库断言失败：请检查数据库是否存在该数据！')
        return failure_count

    @classmethod
    def assert_result(cls, expected_result, response, status_code):
        all_flag = 0
        assert_methods = {
            'code': cls.status_code_assert,
            'contain': cls.contain_assert,
            'eq': cls.equal_assert,
            'ne': cls.not_equal_assert,
            'db': cls.database_assert
        }
        try:
            print(f'打印结果：{expected_result}, 打印类型：', type(expected_result))
            for yq in expected_result:
                for assert_mode, assert_value in yq.items():
                    assert_method: Callable[[Any, Any], int] = assert_methods.get(assert_mode)
                    if assert_method:
                        if assert_mode in ['code', 'db']:
                            flag = assert_method(assert_value, status_code)
                        else:
                            flag = assert_method(assert_value, response)
                        all_flag += flag
                    else:
                        raise AssertTypeError(f'不支持{assert_mode}该断言模式')
        except Exception as exceptions:
            raise exceptions
        assert all_flag == 0, '测试失败！'
        logs.info('测试成功！')